﻿/***************
 * Copyright 2009 (C) 
 * Timur Fanshteyn
 * Blog: http://blog.tfanshteyn.com
 * ************/
using System;
using System.Linq;
using System.Linq.Expressions;
using Tangosol.Net;
using Tangosol.Util;

namespace Coherence.Linq
{
    public class CoherenceQueryProvider  : IQueryProvider
    {
        public INamedCache Cache { get; set; }
        public CoherenceQueryTranslator QueryTranslator { get; set; }
        public CoherenceQueryProvider () : this(null, new CoherenceQueryTranslator())
	    {
	    }

        public CoherenceQueryProvider(INamedCache cache) : this(cache, new CoherenceQueryTranslator())
        {
        }

        public CoherenceQueryProvider(INamedCache cache, CoherenceQueryTranslator queryTranslator)
        {
            Cache = cache;
            QueryTranslator = queryTranslator;
        }

        public IQueryable CreateQuery(Expression expression)
        {
            Type elementType = TypeSystem.GetElementType(expression.Type);
            try
            {
                return (IQueryable)Activator.CreateInstance(typeof(CoherenceQuery<>).MakeGenericType(elementType), new object[] { this, expression });
            }
            catch (System.Reflection.TargetInvocationException tie)
            {
                throw tie.InnerException;
            }
        }

        // Queryable's collection-returning standard query operators call this method.
        public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
        {
            return (IQueryable<TResult>)new CoherenceQuery<TResult>(this, expression);
        }

        #region IQueryProvider Members

        public TResult Execute<TResult>(Expression expression)
        {
            object[] data = (object[]) Execute(expression);
            if (data != null)
                return (TResult)data.Cast<TResult>();
            throw new InvalidOperationException("Null Result");
        }

        public object Execute(Expression expression)
        {
            if (Cache == null)
                throw new InvalidOperationException("Cache is not properly set");

            expression = Evaluator.PartialEval(expression);
            TranslateResult result = QueryTranslator.Translate(expression);

            object[] data = Cache.GetValues(result.Filter);
            Type elementType = TypeSystem.GetElementType(expression.Type);
            if (result.Projector != null)
            {
                Delegate projector = result.Projector.Compile();
                Array custArray = Array.CreateInstance(elementType, data.Length);
                for (int i = 0; i < data.Length; i++)
                {
                    custArray.SetValue(projector.DynamicInvoke(data[i]), i);
                }
                return custArray;
            }
            else
            {
            return data;
            }
        }

        #endregion
    }
}